#include "BloqueListaInvertida.h"
#include <algorithm>


BloqueListaInvertida::BloqueListaInvertida() :
	IBloque(128), _termID(0)
{
}

BloqueListaInvertida::~BloqueListaInvertida() { }


BloqueListaInvertida& BloqueListaInvertida::operator=(const BloqueListaInvertida& bli) {
	this->_termID = bli._termID;
	this->lDocumentos = bli.lDocumentos;

	return *this;
}


unsigned int BloqueListaInvertida::getIDTermino() const{
	return this->_termID;
}

void BloqueListaInvertida::setIDTermino(unsigned int termid){
	this->_termID = termid;
}


void BloqueListaInvertida::guardarIDs(std::vector<unsigned int>& vecDocsIDs){
	this->lDocumentos.reserve( vecDocsIDs.size() );
	this->lDocumentos.assign( vecDocsIDs.begin(), vecDocsIDs.end() );

	// Es posible que lo elementos no esten ordenados
	std::sort(this->lDocumentos.begin(), this->lDocumentos.end());

	// Ahora que estan ordenados puedo chequear que no hayan duplicados
	std::vector<unsigned int>::iterator itDuplicados;
	itDuplicados = std::unique(this->lDocumentos.begin(), this->lDocumentos.end());
	if (itDuplicados != this->lDocumentos.end()){
		this->lDocumentos.erase(itDuplicados, this->lDocumentos.end());
	}

	// Me aseguro que se pueda almacenar el bloque
	if (getSize() >= getTamBloque()) throw std::runtime_error("La lista es demasiado grande para el bloque");
}


void BloqueListaInvertida::recuperarIDs(std::vector<unsigned int>& vecDocsIDs){
	vecDocsIDs.reserve(this->lDocumentos.size());
	vecDocsIDs.assign(this->lDocumentos.begin(), this->lDocumentos.end());
}


std::vector<unsigned int> BloqueListaInvertida::getDistancias() const {
	std::vector<unsigned int> vecDistancias;

	unsigned int docAnterior = 0;
	std::vector<unsigned int>::const_iterator it;
	for (it = this->lDocumentos.begin(); it != this->lDocumentos.end(); ++it){

		// Calcula las distancias entre los id de documentos consecutivos y las guarda
		unsigned int distancia = *it - docAnterior;
		vecDistancias.push_back(distancia);
		docAnterior = *it;
	}

	return vecDistancias;
}


GammaContainer BloqueListaInvertida::comprimirDocumentos() const {
	// Se guardan todos los id de documentos como distancias entre los IDs
	std::vector<unsigned int> vecDistancias = getDistancias();

	// Se comprimen las distancias con Gamma
	GammaContainer g;

	// Evita un problema cuando la lista esta vacia
	if (vecDistancias.size() == 0) return g;

	g << vecDistancias.size();

	std::vector<unsigned int>::const_iterator it;
	for (it = vecDistancias.begin(); it != vecDistancias.end(); ++it) g << *it;

	return g;
}


void BloqueListaInvertida::descomprimirDocumentos(GammaContainer &g){
	// Evita un problema cuando la lista esta vacia
	if (g.is_empty()) return;

	// La cantidad de elementos comprimidos
	unsigned int cant = 0;
	g >> cant;

	unsigned int docAnterior = 0;
	std::vector<unsigned int> vecDocs;
	for (unsigned int i = 0; i < cant; ++i){

		unsigned int distancia = 0;
		g >> distancia;

		unsigned int doc = distancia + docAnterior;
		vecDocs.push_back(doc);
		docAnterior = doc;
	}

	this->lDocumentos.swap(vecDocs);
}


unsigned int BloqueListaInvertida::serialize(char* &arr){
	// Comprime los docs ids con gamma
	GammaContainer g = comprimirDocumentos();

	this->serInator.reset();
	this->serInator.serializar(arr, this->_termID);
	this->serInator.serializar(arr, static_cast<char>( g.getCantBytes() ));
	this->serInator.serializar(arr, g);

	return this->serInator.getBufferSize();
}


void BloqueListaInvertida::deSerialize(char* arr){
	char uc = 0;
	GammaContainer g;

	this->serInator.reset();
	this->serInator.desSerializar(arr, this->_termID);
	this->serInator.desSerializar(arr, uc);
	this->serInator.desSerializar(arr, g);

	descomprimirDocumentos(g);
}


unsigned int BloqueListaInvertida::getSize() const{
	GammaContainer g = comprimirDocumentos();
	unsigned int tam = sizeof(this->_termID) + sizeof(char);

	return tam + g.getCantBytes();
}


void BloqueListaInvertida::agregarID(unsigned int id){
	std::vector<unsigned int>::const_iterator it;
	for (it = this->lDocumentos.begin(); it != this->lDocumentos.end(); ++it){
		// El id ya esta en la lista
		if ( (*it) == id ) return;
	}

	// El id no esta en la lista, entonces lo inserta al final y lo ordena
	this->lDocumentos.push_back(id);
	std::sort(this->lDocumentos.begin(), this->lDocumentos.end());
}


void BloqueListaInvertida::eliminarID(unsigned int id){
	std::vector<unsigned int>::iterator it;
	for (it = this->lDocumentos.begin(); it != this->lDocumentos.end(); ++it){
		if ( (*it) == id ){
			// Encontré el id, hay que borrarlo
			this->lDocumentos.erase(it);
			return;
		}
	}

	// Si no esta en la lista no pasa nada
}



